'use client';

import mqtt, { MqttClient, IClientOptions, Packet } from 'mqtt';
import { 
  MqttConfig, 
  MqttConnectionStatus, 
  MqttEventHandler, 
  MqttStatusChangeCallback,
  MqttSubscription,
  MqttPacket
} from './types';

/**
 * MQTT服务类
 * 用于管理MQTT连接、订阅和消息处理
 */
class MqttService {
  private client: MqttClient | null = null;
  private config: MqttConfig | null = null;
  private status: MqttConnectionStatus = MqttConnectionStatus.DISCONNECTED;
  private statusChangeCallbacks: MqttStatusChangeCallback[] = [];
  private messageHandlers: Map<string, MqttEventHandler[]> = new Map();
  private reconnectTimer: NodeJS.Timeout | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;
  private reconnectInterval = 3000; // 重连间隔(毫秒)

  /**
   * 初始化MQTT配置
   * @param config MQTT连接配置
   */
  public initialize(config: MqttConfig): void {
    this.config = config;
    console.log('MQTT服务初始化完成，配置:', config);
  }

  /**
   * 连接到MQTT服务器
   */
  public connect(): void {
    if (!this.config) {
      console.error('MQTT配置未初始化');
      this.updateStatus(MqttConnectionStatus.ERROR, new Error('MQTT配置未初始化'));
      return;
    }

    // 如果已经连接，则不执行任何操作
    if (this.client && this.isConnected()) {
      console.log('MQTT客户端已连接，无需重新连接');
      return;
    }

    // 如果有客户端但未连接（断开或错误状态），则先断开
    if (this.client) {
      console.log('MQTT客户端存在但未正常连接，断开后重新连接');
      this.client.end(true);
      this.client = null;
    }

    this.updateStatus(MqttConnectionStatus.CONNECTING);
    
    const { host, port, protocol, username, password, clientId, path } = this.config;
    const url = `${protocol}://${host}:${port}${path || '/mqtt'}`;
    
    const options: IClientOptions = {
      clientId: clientId || `dashboard_${Date.now()}`,
      username,
      password,
      clean: true,
      reconnectPeriod: 0, // 禁用自动重连，使用我们自己的重连逻辑
      connectTimeout: 5000, // 连接超时设置为5秒
    };

    try {
      console.log(`尝试连接到MQTT服务器: ${url}`);
      this.client = mqtt.connect(url, options);
      
      // 设置事件监听器
      this.client.on('connect', this.handleConnect.bind(this));
      this.client.on('reconnect', this.handleReconnect.bind(this));
      this.client.on('error', this.handleError.bind(this));
      this.client.on('offline', this.handleOffline.bind(this));
      this.client.on('close', this.handleClose.bind(this));
      this.client.on('message', this.handleMessage.bind(this));
    } catch (error) {
      console.error('MQTT连接失败:', error);
      this.updateStatus(MqttConnectionStatus.ERROR, error instanceof Error ? error : new Error('未知连接错误'));
      this.scheduleReconnect();
    }
  }

  /**
   * 断开MQTT连接
   */
  public disconnect(): void {
    if (this.client) {
      this.client.end(true);
      this.client = null;
      
      // 清除重连定时器
      if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = null;
      }
      
      this.updateStatus(MqttConnectionStatus.DISCONNECTED);
      console.log('MQTT连接已断开');
    }
  }

  /**
   * 检查主题是否已订阅
   * @param topic 主题
   */
  public isTopicSubscribed(topic: string): boolean {
    return this.messageHandlers.has(topic) && this.messageHandlers.get(topic)!.length > 0;
  }

  /**
   * 订阅主题
   * @param subscription 订阅主题配置
   * @param handler 消息处理函数
   */
  public subscribe(subscription: MqttSubscription, handler: MqttEventHandler): void {
    if (!this.client || !this.isConnected()) {
      console.error('MQTT客户端未连接，无法订阅主题');
      return;
    }

    const { topic, qos = 0 } = subscription;
    
    // 检查处理函数是否已注册
    let handlerAlreadyRegistered = false;
    if (this.messageHandlers.has(topic)) {
      const handlers = this.messageHandlers.get(topic)!;
      handlerAlreadyRegistered = handlers.includes(handler);
    }
    
    // 注册消息处理函数
    if (!this.messageHandlers.has(topic)) {
      this.messageHandlers.set(topic, []);
    }
    
    const handlers = this.messageHandlers.get(topic)!;
    if (!handlers.includes(handler)) {
      handlers.push(handler);
    } else {
      console.log(`处理函数已注册到主题: ${topic}`);
    }
    
    // 如果处理函数已注册但想确保订阅，不打印重复日志
    if (handlerAlreadyRegistered) {
      return;
    }
    
    // 订阅主题
    this.client.subscribe(topic, { qos }, (err) => {
      if (err) {
        console.error(`订阅主题失败: ${topic}`, err);
        return;
      }
      
      console.log(`成功订阅主题: ${topic}, QoS: ${qos}`);
    });
  }

  /**
   * 取消订阅主题
   * @param topic 主题
   * @param handler 可选，特定的消息处理函数。如果未提供，将移除该主题的所有处理函数
   */
  public unsubscribe(topic: string, handler?: MqttEventHandler): void {
    if (!this.client || !this.isConnected()) {
      console.warn('MQTT客户端未连接，无法取消订阅');
      return;
    }

    // 取消在MQTT服务器上的订阅
    this.client.unsubscribe(topic, (err) => {
      if (err) {
        console.error(`取消订阅主题失败: ${topic}`, err);
        return;
      }
      
      console.log(`成功取消订阅主题: ${topic}`);
    });

    // 移除消息处理函数
    if (!this.messageHandlers.has(topic)) return;
    
    if (handler) {
      // 移除特定处理函数
      const handlers = this.messageHandlers.get(topic)!;
      const index = handlers.indexOf(handler);
      if (index !== -1) handlers.splice(index, 1);
      if (handlers.length === 0) this.messageHandlers.delete(topic);
    } else {
      // 移除所有处理函数
      this.messageHandlers.delete(topic);
    }
  }

  /**
   * 发布消息到主题
   * @param topic 主题
   * @param message 消息内容
   * @param qos 服务质量等级
   * @param retain 是否保留消息
   */
  public publish(topic: string, message: string | Buffer, qos: 0 | 1 | 2 = 0, retain: boolean = false): void {
    if (!this.client || !this.isConnected()) {
      console.error('MQTT客户端未连接，无法发布消息');
      return;
    }

    this.client.publish(topic, message, { qos, retain }, (err) => {
      if (err) {
        console.error(`发布消息到主题失败: ${topic}`, err);
        return;
      }
      
      console.log(`成功发布消息到主题: ${topic}`);
    });
  }

  /**
   * 注册状态变化回调函数
   * @param callback 状态变化回调
   */
  public onStatusChange(callback: MqttStatusChangeCallback): void {
    if (!this.statusChangeCallbacks.includes(callback)) {
      this.statusChangeCallbacks.push(callback);
    }
  }

  /**
   * 移除状态变化回调函数
   * @param callback 状态变化回调
   */
  public offStatusChange(callback: MqttStatusChangeCallback): void {
    const index = this.statusChangeCallbacks.indexOf(callback);
    if (index !== -1) {
      this.statusChangeCallbacks.splice(index, 1);
    }
  }

  /**
   * 获取当前连接状态
   */
  public getStatus(): MqttConnectionStatus {
    return this.status;
  }

  /**
   * 检查是否已连接
   */
  public isConnected(): boolean {
    return this.status === MqttConnectionStatus.CONNECTED;
  }

  // 私有方法：处理连接成功事件
  private handleConnect(): void {
    console.log('MQTT连接成功');
    this.updateStatus(MqttConnectionStatus.CONNECTED);
    this.reconnectAttempts = 0; // 重置重连尝试次数
  }

  // 私有方法：处理重连事件
  private handleReconnect(): void {
    console.log('MQTT正在重连...');
    this.updateStatus(MqttConnectionStatus.RECONNECTING);
  }

  // 私有方法：处理错误事件
  private handleError(error: Error): void {
    console.error('MQTT连接错误:', error);
    this.updateStatus(MqttConnectionStatus.ERROR, error);
    this.scheduleReconnect();
  }

  // 私有方法：处理离线事件
  private handleOffline(): void {
    console.log('MQTT客户端离线');
    this.updateStatus(MqttConnectionStatus.DISCONNECTED);
    this.scheduleReconnect();
  }

  // 私有方法：处理关闭事件
  private handleClose(): void {
    console.log('MQTT连接已关闭');
    this.updateStatus(MqttConnectionStatus.DISCONNECTED);
    this.scheduleReconnect();
  }

  // 私有方法：处理接收到的消息
  private handleMessage(topic: string, message: Buffer, packet: Packet): void {
    console.log(`收到来自主题 ${topic} 的消息:`, message.toString());
    
    // 调用针对该主题注册的所有处理函数
    if (this.messageHandlers.has(topic)) {
      const handlers = this.messageHandlers.get(topic)!;
      handlers.forEach(handler => {
        try {
          // 将原始Packet转换为我们的MqttPacket类型
          const mqttPacket: MqttPacket = {
            cmd: packet.cmd,
            topic: topic,
            payload: message
          };
          
          handler(topic, message, mqttPacket);
        } catch (error) {
          console.error(`处理消息时出错:`, error);
        }
      });
    }
  }

  // 私有方法：安排重新连接
  private scheduleReconnect(): void {
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
    
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error(`已达到最大重连尝试次数 (${this.maxReconnectAttempts})，停止重连`);
      this.updateStatus(MqttConnectionStatus.DISCONNECTED);
      return;
    }
    
    this.reconnectAttempts++;
    const delay = this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1); // 指数退避
    console.log(`计划在 ${delay}ms 后进行第 ${this.reconnectAttempts} 次重连`);
    
    this.reconnectTimer = setTimeout(() => {
      console.log(`开始第 ${this.reconnectAttempts} 次重连`);
      this.connect();
    }, delay);
  }

  // 私有方法：更新状态并通知回调
  private updateStatus(status: MqttConnectionStatus, error?: Error): void {
    if (this.status === status) return; // 状态没有变化，不需要通知
    
    this.status = status;
    
    // 通知所有回调
    this.statusChangeCallbacks.forEach(callback => {
      try {
        callback(status, error);
      } catch (callbackError) {
        console.error('状态变更回调执行出错:', callbackError);
      }
    });
  }
}

// 导出单例实例
export const mqttService = new MqttService(); 